Перейти к основному содержимому

6.07. Технический дизайн

Аналитику Архитектору Руководителю Техническому писателю

Технический дизайн

Технический дизайн — это фундаментальный этап проектирования программных систем, предшествующий непосредственной реализации. Он представляет собой систематизированный процесс формализованного описания решений, принимаемых на стадии архитектурного и детального проектирования, с целью обеспечения согласованного понимания задачи, её границ и методов реализации всеми участниками проекта.

Название «технический дизайн» может вызывать ложные ассоциации с инженерной графикой или промышленным проектированием, где под техническим дизайном подразумевают чертежи изделий или спецификации производственных процессов. В информационных технологиях у этого термина иной смысл: речь идёт не о внешнем виде или пользовательском интерфейсе, а о внутренней организации системы — её структуре, связях, поведении компонентов и принципах взаимодействия. Технический дизайн сосредоточен на функциональности, надёжности, масштабируемости и сопровождаемости, а не на эстетике или удобстве восприятия конечным пользователем.

Сокращение ТД в отечественной практике часто интерпретируется неоднозначно: одни специалисты употребляют его как техническая документация, другие — как технический дизайн. Однако в профессиональном IT-контексте, особенно в среде продуктовых команд и при разработке сложных систем, под ТД подразумевается именно документ технического проектирования — design document, technical design specification, solution design. Это не набор инструкций по эксплуатации, не описание API в формате OpenAPI и не сборник инсталляционных заметок. Это — интеллектуальный артефакт, фиксирующий логику принятых решений и обеспечивающий основу для последующих этапов: кодирования, тестирования, развёртывания и поддержки.

Роль и значение технического дизайна в жизненном цикле проекта

Любая сколь-нибудь значимая программная система, выходящая за пределы одноразового скрипта или экспериментального прототипа, требует планирования. Отказ от формального проектирования часто обосновывается стремлением к скорости — «давайте быстрее напишем, а потом разберёмся». Такой подход допустим только при крайне ограниченных временных горизонтах и в условиях полной изолированности кода. Как только система приобретает пользователей, зависимости, требования к стабильности и поддержке — спонтанная разработка становится источником технического долга, который рано или поздно перерастает в архитектурный кризис.

Технический дизайн служит катализатором инженерной зрелости команды. Он позволяет:

  • Сверить понимание задачи у всех заинтересованных сторон — от бизнес-аналитиков до инфраструктурных инженеров;
  • Обнаружить противоречия в требованиях до начала реализации;
  • Оценить альтернативные пути решения и обосновать выбор одного из них;
  • Разделить ответственность за отдельные подсистемы;
  • Создать основу для документации, тест-планирования и последующей эксплуатации.

Документ технического дизайна не является статичной формальностью. Это живой артефакт, который рождается в ходе дискуссий, обсуждений и уточнений, проходит итерации ревью и уточняется в процессе реализации. В идеале — после завершения задачи он актуализируется и становится частью технической документации продукта.

Кто отвечает за создание технического дизайна

Хотя в практике встречается передача этой работы исключительно архитектору или техлиду, в современных командах ответственность за технический дизайн чаще всего лежит на разработчике, который будет реализовывать задачу. Причин несколько.

Во-первых, разработчик обладает наиболее полным контекстом: он знаком с кодовой базой, инструментами, интеграциями и неявными ограничениями системы. Архитектор или аналитик могут предложить высокоуровневую стратегию, но только реализатор способен оценить практическую осуществимость того или иного подхода в рамках конкретного стека и текущего состояния проекта.

Во-вторых, процесс проектирования — это не просто запись решений, а форма мышления перед кодированием. Он заставляет разработчика выйти из режима оперативного выполнения и перейти в режим стратегического планирования. Это снижает когнитивную нагрузку в фазе реализации и повышает качество кода.

В-третьих, включение разработчика в процесс проектирования стимулирует чувство ответственности и вовлечённости. Человек, который сам спроектировал решение, с большей вероятностью будет придерживаться принятых решений и стремиться к их корректной реализации.

Аналитик, в свою очередь, может участвовать в подготовке входных данных — формулировании требований, описании бизнес-процессов, выявлении ограничений — но сам технический дизайн пишется инженером. Это ключевое различие между бизнес-анализом и техническим проектированием: первое отвечает на вопрос «что должно быть», второе — на вопрос «как это сделать».

Структура документа технического дизайна

Как уже отмечалось, технический дизайн — это не литературное произведение, а инженерный документ, и его структура должна быть предсказуемой, чтобы сократить время на ознакомление и повысить эффективность ревью. Ниже приведена рекомендуемая структура, основанная на многолетней практике применения в командах различного масштаба и профиля. Она модульна: не все разделы обязательны для каждой задачи, но их наличие позволяет системно рассмотреть проект с разных углов.

1. Титульный лист и служебная информация

Хотя в Markdown-среде титульный лист может отсутствовать в явном виде, минимальный набор метаданных необходим:

  • Название документа (с указанием функциональной области и задачи, например: «ТД: Рефакторинг модуля отчётов — переход на event-driven архитектуру»);
  • Автор (разработчик);
  • Дата создания и последней редакции;
  • Статус документа (черновик, на ревью, одобрен, актуализирован).

2. Оглавление

Для документов объёмом свыше одной экранной страницы оглавление критически важно. Оно обеспечивает навигацию и помогает читателю понять структуру мышления автора.

3. История изменений

Таблица вида:

ВерсияДатаАвторИзменения
0.12025-11-20Т.В. ТагировПервоначальный черновик
0.22025-11-22Т.В. ТагировДобавлены диаграммы, уточнены требования

История изменений позволяет отслеживать эволюцию проекта и понимать, какие корректировки были внесены по итогам обсуждений.

4. Лист согласования

Формальный перечень лиц, чьё одобрение требуется перед началом реализации. Включает:

  • Имя, роль, дата согласования;
  • Факультативно — ссылка на комментарий или тикет в системе трекинга.

Согласование не заменяет техническое ревью, но фиксирует формальную готовность команды к движению вперёд.

5. Связанные документы

Ссылки на:

  • Бизнес-требования (например, User Story, epic, product brief);
  • Существующую документацию (архитектурные диаграммы, описания API, RFC);
  • Технические стандарты организации (например, «Стандарт именования REST-эндпоинтов», «Политика управления секретами»);
  • Юридические и нормативные акты (если проект затрагивает обработку персональных данных, финансовые операции и пр.).

Этот раздел создаёт информационный контекст и облегчает вхождение в проект новым участникам.

6. Ограничения и допущения

Здесь фиксируются условия, в рамках которых принимаются проектируемые решения. Например:

  • Допущение: «Система будет развёрнута в едином регионе облака; кросс-региональная репликация не рассматривается на данном этапе»;
  • Ограничение: «Использование новых языковых фич языка C# 12 запрещено из-за требований к совместимости со средой выполнения версии 6.0»;
  • Ограничение: «Интеграция с внешней системой X возможна только по протоколу SOAP 1.1; REST API отсутствует».

Этот раздел защищает от некорректных критических замечаний вроде: «А почему не использовали Kafka?» — если изначально оговорено, что шина сообщений не входит в зону ответственности текущей задачи.

7. Требования

Требования — основа всего технического дизайна. Они должны быть сформулированы до описания решения, поскольку именно они определяют его корректность.

Функциональные требования

Описывают что должна делать система:

  • Какие операции поддерживает;
  • Какие сценарии использования реализуются;
  • Какие данные принимаются и генерируются;
  • Какие события обрабатываются и какие триггеры используются.

Важно: формулировки должны быть проверяемыми. «Система должна быстро обрабатывать запросы» — некорректно. «Система должна обрабатывать 95% запросов менее чем за 300 мс при нагрузке до 500 RPS» — корректно.

Нефункциональные требования

Определяют как хорошо система должна выполнять функции:

  • Производительность: латентность, пропускная способность, время отклика;
  • Масштабируемость: ожидаемый рост нагрузки, допустимые пределы горизонтального/вертикального масштабирования;
  • Надёжность и отказоустойчивость: SLO/SLI (например, 99.95% uptime), стратегии retry, fallback, circuit breaking;
  • Безопасность: требования к аутентификации, авторизации, аудиту, шифрованию данных при передаче и в покое;
  • Сопровождаемость: требования к логированию, мониторингу, трассировке;
  • Совместимость: поддержка браузеров, ОС, версий зависимостей;
  • Юридические требования: соответствие GDPR, 152-ФЗ, PCI DSS и пр.

Нефункциональные требования часто подвергаются недооценке на ранних этапах, но именно их нарушение приводит к самым дорогостоящим переделкам после запуска.


Структурное программирование как основа технического проектирования

Структурное программирование — не просто исторический этап в эволюции языков, а фундаментальная дисциплина мышления, без которой невозможен осмысленный технический дизайн. Сформировавшись в 1960–1970-х годах в ответ на кризис программного обеспечения, когда рост сложности систем опережал возможности по их контролю, структурный подход ввёл ключевой принцип: управляемость потока управления через ограниченный набор управляющих конструкций — последовательность, ветвление и цикл.

В контексте технического дизайна структурное программирование проявляется не на уровне синтаксиса кода, а на уровне проектирования модулей и процессов. Оно требует:

  • Чёткого выделения входов и выходов каждого компонента;
  • Отсутствия неуправляемых переходов (в современной терминологии — избегания скрытых побочных эффектов, неявных зависимостей, глобального состояния там, где это не оправдано);
  • Декомпозиции сложных задач на иерархию подзадач с единственной точкой входа и единственной точкой выхода (принцип single-entry, single-exit);
  • Контролируемой передачи управления через явные вызовы и возвраты, а не через события, исключения или прерывания без строгого протокола.

Эти принципы напрямую влияют на качество технического дизайна. Например, если в описании взаимодействия компонентов встречается фраза «модуль A отправляет сигнал, на который может реагировать любой другой модуль» без чёткого перечня получателей и контракта сигнала — это признак нарушения структурного подхода. Такая система трудно тестируема, плохо поддаётся анализу потока данных и склонна к нестабильному поведению при изменении окружения.

Структурное программирование также лежит в основе процедурного проектирования, где основной единицей становится процедура или функция с чётко определённой сигнатурой и контрактом. Даже в объектно-ориентированных и реактивных системах этот подход сохраняет силу: метод класса, обработчик события или эффект в state-менеджере — всё это должны быть структурированными, тестируемыми, детерминированными (в пределах внешних зависимостей) блоками.

Технический дизайн, построенный без учёта структурных принципов, рано или поздно превращается в описание как есть, а не как должно быть. Он фиксирует хаос, а не упорядочивает его.

Стандарт проектирования и стандарт дизайна

В профессиональной среде термины «стандарт проектирования» и «стандарт дизайна» часто используются как синонимы, но между ними есть принципиальное различие.

Стандарт проектирования — это набор обязательных правил и ограничений, регулирующих форму и процесс создания технических артефактов. Он отвечает на вопросы:

  • Какие разделы должен содержать документ технического дизайна и в каком порядке?
  • Какие диаграммы допустимы, какие нотации применять (например, UML 2.5, C4-Model, ArchiMate)?
  • Как именовать компоненты, классы, переменные в спецификациях?
  • Как фиксировать зависимости между модулями?
  • Какие метрики и параметры должны быть указаны в разделе «Производительность»?

Стандарт проектирования — это часть корпоративной инженерной культуры. Он обеспечивает единообразие, сокращает время на чтение чужих документов и упрощает автоматизацию (например, генерацию документации из исходников или проверку соответствия политике безопасности). Отклонения от стандарта требуют явного обоснования и согласования.

Стандарт дизайна, напротив, регулирует содержание решений — что должно быть сделано, а не как описано. Он включает:

  • Требования к архитектурным стилям (например, «все новые микросервисы должны быть построены по принципу API-first и использовать gRPC для внутреннего взаимодействия»);
  • Ограничения на выбор технологий («хранение конфиденциальных данных возможно только в PostgreSQL с включённым TDE; использование SQLite на продакшене запрещено»);
  • Паттерны поведения («все операции, изменяющие состояние, должны быть идемпотентными»);
  • Политики безопасности («все внешние вызовы должны использовать mTLS; API-ключи недопустимы»).

Стандарт дизайна — это проявление архитектурной стратегии. Он эволюционирует медленнее, чем стандарт проектирования, и отражает долгосрочные цели по управлению сложностью, снижению рисков и обеспечению совместимости.

Важно: стандарты не должны быть догмой. Их назначение — сократить вариативность там, где она не приносит пользы, а не подавить инженерную инициативу. Поэтому хороший стандарт включает раздел «Исключения и процедура отступления» — чёткий путь для легитимного нарушения правила с фиксацией мотивации и последствий.

Техническая инфраструктура как часть технического дизайна

Технический дизайн не ограничивается описанием кода. Он в обязательном порядке должен учитывать техническую инфраструктуру — совокупность ресурсов, сервисов и процессов, обеспечивающих выполнение программного кода. Это не просто «где будет запущено приложение», а системное описание окружения, в котором система будет функционировать, развиваться и поддерживаться.

Инфраструктурные аспекты, которые должны быть отражены в ТД:

  • Вычислительные ресурсы: тип и количество инстансов, требования к CPU/RAM/диску, необходимость GPU или специализированных ускорителей;
  • Сетевые требования: зоны доступности, требования к задержкам между компонентами, необходимость изоляции (VPC, network policies), QoS для трафика;
  • Хранилища данных: типы баз данных (OLTP, OLAP, кэш, объектное хранилище), модели репликации, политики резервного копирования и восстановления;
  • Сервисы платформы: использование message broker’ов (Kafka, RabbitMQ), очередей (SQS, Redis Streams), систем оркестрации (Kubernetes), service mesh (Istio, Linkerd);
  • Инженерные сервисы: CI/CD-конвейеры, системы логирования (ELK, Loki), метрик (Prometheus, Datadog), трассировки (Jaeger, Zipkin), alerting;
  • Политики безопасности инфраструктуры: управление секретами (Vault, AWS Secrets Manager), scanning образов, политики доступа (RBAC, ABAC), интеграция с IAM;
  • Процессы эксплуатации: процедуры развёртывания, отката, масштабирования, восстановления после сбоев.

Ключевой принцип: инфраструктурные решения должны быть обоснованы требованиями, а не удобством или привычкой. Например, если в нефункциональных требованиях указано «время восстановления после отказа не более 5 минут», то в инфраструктурном разделе должно быть описано, как именно это достигается — через активно-активную репликацию, автоматический failover, pre-warmed standby-ноды и пр.

Отсутствие инфраструктурного раздела в ТД — признак неготовности команды к production-эксплуатации. Такой документ описывает «идеальную лабораторию», а не реальный продукт.

Архитектура программного обеспечения и слоистая организация

Архитектура программного обеспечения — это совокупность фундаментальных структурных решений, определяющих организацию системы, её компоненты, их взаимодействие и принципы эволюции. В техническом дизайне архитектура описывается не как абстрактная теория, а как рабочая модель, пригодная для реализации.

Одним из наиболее устойчивых подходов к описанию архитектуры является слоистая (layered) модель. Слои — это логические границы, разделяющие систему по уровням ответственности. Каждый слой может зависеть только от слоёв, расположенных ниже него. Такая организация обеспечивает:

  • Чёткое разделение зон ответственности;
  • Возможность замены реализации одного слоя без влияния на вышестоящие;
  • Упрощение тестирования (мокирование нижележащих слоёв);
  • Повышение сопровождаемости.

Типичные слои в веб-приложении:

  1. Презентационный слой (UI / Presentation Layer) — отвечает за отображение и взаимодействие с пользователем. Включает веб-интерфейс, мобильные клиенты, CLI-утилиты. Здесь формируются запросы к нижележащим слоям и отображаются результаты.
  2. Слой прикладной логики (Application Layer / Service Layer) — реализует сценарии использования, оркестрирует вызовы доменных сервисов, управляет транзакциями. Часто реализуется как набор use-case классов или command handlers.
  3. Доменный слой (Domain Layer) — ядро системы, содержит бизнес-правила, сущности, доменные сервисы, политики. Не зависит от внешних технологий, баз данных, фреймворков. Должен быть тестируем без запуска инфраструктуры.
  4. Слой доступа к данным (Persistence / Infrastructure Layer) — реализует интерфейсы, объявленные в доменном слое: репозитории, драйверы внешних API, адаптеры для очередей и пр.

Важно: слои — это логическое, а не физическое разделение. Один и тот же процесс может содержать код нескольких слоёв. Однако архитектурные принудительные механизмы (например, dependency inversion в сочетании с архитектурными тестами вроде ArchUnit) должны обеспечивать соблюдение зависимостей только в разрешённом направлении.

В распределённых системах слои могут проецироваться на физические границы: например, доменный и прикладной слои — в микросервисе, презентационный — во фронтенд-приложении, доступ к данным — в отдельном data service’е. Но логическая иерархия сохраняется.

Объекты и классы в техническом дизайне

В объектно-ориентированных системах (а также в системах, использующих объектную модель для анализа, даже если реализация — функциональная) технический дизайн включает детальное проектирование объектной модели. Это не просто перечень классов, а описание того, как система думает о предметной области.

Каждый значимый объект в техническом дизайне описывается по четырём ключевым аспектам — так называемым четырём столпам объектного проектирования:

1. Атрибуты — что знает объект

Атрибуты отражают внутреннее состояние объекта. Важно различать:

  • Идентифицирующие атрибуты (например, id, uuid) — определяют уникальность экземпляра;
  • Описательные атрибуты (например, name, email, createdAt) — несут семантическую нагрузку;
  • Производные атрибуты (например, age, вычисляемый из birthDate) — должны быть либо вычисляемыми «на лету», либо сопровождаться правилами инвалидации кэша;
  • Внешние ссылки (например, orderId в объекте Invoice) — должны сопровождаться описанием целостности: каскадное удаление, soft delete, отложенная загрузка.

Атрибуты указываются с типами, но не в синтаксисе конкретного языка (например, не string email), а в нейтральной форме: «email — строка в формате RFC 5322, необязательный, уникальный в рамках организации».

2. Методы — что делает объект

Методы описывают поведение. Различают:

  • Команды — методы, изменяющие состояние (placeOrder(), cancelSubscription());
  • Запросы — методы, только читающие состояние (getTotalAmount(), isValid());
  • Конструкторы и фабрики — способы создания валидного экземпляра;
  • Служебные методы (например, toString(), equals(), hashCode()) — при необходимости.

Для каждого метода указывается:

  • Сигнатура (имя, параметры с типами и семантикой);
  • Постусловия (что гарантируется после выполнения);
  • Предусловия (что должно быть истинно до вызова);
  • Возможные исключения и ошибки.

Например: «Метод processPayment(amount: Money, currency: Currency) вызывает внешний payment gateway. В случае успеха — фиксирует платёж в истории и переводит заказ в статус PAID. В случае временной ошибки (5xx от шлюза) — повторяет до 3 раз с экспоненциальной задержкой. В случае постоянной ошибки (4xx) — выбрасывает PaymentFailedException с кодом причины».

3. Статусы — изменения, происходящие в результате потока процесса

Статусы отражают жизненный цикл объекта. Они не просто перечисление значений (DRAFT, PUBLISHED, ARCHIVED), а состояния конечного автомата, с чётко определёнными переходами.

Для каждого статуса указывается:

  • Допустимые предшествующие статусы;
  • Условия перехода (какие действия, события или временные интервалы инициируют переход);
  • Побочные эффекты (например, «переход в COMPLETED генерирует событие OrderCompleted, запускает расчёт бонусов»);
  • Ограничения на операции в данном состоянии (например, «в статусе CANCELLED недопустимы изменения состава заказа»).

Диаграмма состояний (state machine diagram) — обязательный артефакт для объектов со сложной логикой жизненного цикла.

4. События — реакции на внешний мир

События — это асинхронные уведомления о произошедших фактах. В техническом дизайне важно различать:

  • Доменные события (OrderPlaced, UserRegistered) — отражают значимые для бизнеса изменения, публикуются после фиксации транзакции;
  • Интеграционные события — адаптированы под внешние системы, могут содержать дополнительные поля или иной формат;
  • Системные события (ServiceStarted, ConfigReloaded) — касаются инфраструктурных аспектов.

Каждое событие описывается:

  • Именем в прошедшем времени (событие — это факт);
  • Набором полей с семантикой;
  • Гарантиями доставки (at-least-once, at-most-once, exactly-once);
  • Местом публикации (topic, exchange, SNS topic);
  • Требованиями к идемпотентности обработчиков.

Событийная модель позволяет выстраивать слабосвязанные, масштабируемые системы, но требует чёткого управления согласованностью (например, через transactional outbox или dual-write с компенсирующими транзакциями).


Диаграммы UML как средство формализации технического дизайна

UML (Unified Modeling Language) — не инструмент автоматической генерации кода и не обязательный атрибут «правильного» проектирования. Это язык визуального моделирования, позволяющий точно, недвусмысленно и компактно выразить сложные структурные и поведенческие отношения в системе. В техническом дизайне UML применяется избирательно: не ради соблюдения формальностей, а для решения конкретных коммуникационных и аналитических задач.

Важно понимать: UML — это средство, а не цель. Его ценность измеряется не количеством диаграмм, а тем, насколько они сокращают неопределённость и ускоряют достижение консенсуса. Диаграммы, которые никто не читает или которые дублируют текст, приносят больше вреда, чем пользы.

Ниже — ключевые типы диаграмм, имеющие практическую ценность в техническом дизайне, с указанием их назначения и границ применимости.

Диаграмма классов (Class Diagram)

Наиболее часто используемая диаграмма в контексте детального проектирования. Она отображает:

  • Статическую структуру объектной модели: классы, их атрибуты, методы;
  • Отношения между классами: ассоциации (с указанием кратности: 1, 0..*, 1..1), агрегации, композиции, наследование, реализация интерфейсов;
  • Границы модулей или пакетов (через фреймы «package»).

В техническом дизайне диаграмма классов не должна включать все классы системы. Она фокусируется на ядре задачи: на тех сущностях, которые изменяются, добавляются или требуют согласования интерфейсов. Например, при рефакторинге модуля оплаты диаграмма включает Payment, PaymentMethod, PaymentGatewayAdapter, PaymentProcessor, но не User или Product, если их интерфейсы не затрагиваются.

Критически важно: связи должны отражать реальные зависимости времени компиляции или выполнения, а не логические ассоциации. Если класс A вызывает метод класса B через интерфейс, на диаграмме должна быть стрелка реализации от B к интерфейсу и зависимость от A к интерфейсу — не прямая ассоциация A–B.

Диаграмма последовательностей (Sequence Diagram)

Используется для описания динамики взаимодействия в рамках конкретного сценария: обработки запроса, выполнения бизнес-процесса, реакции на событие. Показывает:

  • Участников (акторов, объекты, внешние системы) как вертикальные lifeline’ы;
  • Сообщения (вызовы методов, события, HTTP-запросы) как горизонтальные стрелки;
  • Активации (прямоугольники на lifeline), отражающие период выполнения;
  • Альтернативные потоки (фреймы alt, opt, loop).

Применяется в техническом дизайне для:

  • Уточнения сложных сценариев, где задействовано несколько систем;
  • Выявления скрытых синхронных блокировок («цепочки вызовов», где A→B→C→D синхронно);
  • Визуализации границ транзакций и точек компенсации.

Пример: при проектировании подтверждения заказа диаграмма последовательностей покажет, что OrderService вызывает InventoryService.reserve(), затем PaymentService.charge(), и только при успехе обоих — OrderRepository.updateStatus(), иначе — InventoryService.cancelReservation(). Это делает явной необходимость компенсирующей логики.

Диаграмма состояний (State Machine Diagram)

Как уже отмечалось, жизненный цикл объекта с несколькими статусами требует формализации. Диаграмма состояний:

  • Отображает состояния (состояния) как закруглённые прямоугольники;
  • Переходы — как стрелки с надписью событие [условие] / действие;
  • Начальное () и конечное (●○) состояния.

Используется, когда логика переходов сложна: например, заказ может быть в статусе PENDING_PAYMENT, но если платёж не поступил за 30 минут — автоматически перейти в EXPIRED; или если администратор вручную подтвердил — перейти в CONFIRMED минуя оплату. Текстовое описание таких правил легко становится противоречивым; диаграмма делает их проверяемыми.

Диаграмма компонентов (Component Diagram)

Отражает физическое или логическое членение системы на автономные части:

  • Компоненты (модули, микросервисы, библиотеки);
  • Интерфейсы (provided и required);
  • Зависимости между компонентами.

В техническом дизайне компонентная диаграмма особенно важна при:

  • Выделении границ микросервисов;
  • Описании новых модулей в monorepo;
  • Планировании поставки отдельных артефактов (библиотек, SDK).

Она отвечает на вопрос: «Что можно собрать, развернуть и заменить независимо?».


UML не является обязательным стандартом. Альтернативные нотации (C4-Model для архитектуры, BPMN для бизнес-процессов) могут быть предпочтительнее в зависимости от задачи. Главное — соблюдение принципа: диаграмма должна уменьшать энтропию знаний, а не увеличивать её.

Серверная архитектура: контроллер, модель, представление — и за её пределами

В техническом дизайне часто возникает необходимость описать структуру серверного приложения. Хотя паттерн MVC (Model-View-Controller) стал почти синонимом веб-разработки, его буквальное применение в современных системах требует уточнения и адаптации.

Контроллер (Controller)

Контроллер — это точка входа для внешних запросов. Его обязанности:

  • Приём и валидация входных данных (HTTP-заголовки, параметры пути, тело запроса);
  • Преобразование входных данных в объекты предметной области (DTO → Domain Object);
  • Вызов соответствующего сервиса или use-case;
  • Обработка исключений и формирование HTTP-ответа (код статуса, заголовки, тело).

Важно: контроллер не должен содержать бизнес-логики. Даже простая проверка вида «если сумма > 0» принадлежит доменному слою. Контроллер — это адаптер между протоколом (HTTP, gRPC) и прикладной логикой.

Модель (Model)

Термин «модель» в контексте MVC исторически трактуется неоднозначно. В техническом дизайне следует явно разделять:

  • Доменная модель — ядро бизнес-логики: сущности, агрегаты, доменные сервисы, политики. Не знает о базах данных, API, фреймворках.
  • Модель данных (Data Model) — структура хранения: таблицы, документы, схемы. Часто отражает доменную модель, но может отличаться из-за требований нормализации, производительности или особенностей СУБД.

В современных системах доменная модель и модель данных разделяются явно: через паттерны Repository, Data Mapper, DTO. Технический дизайн должен указывать, где и как происходит это преобразование.

Дисплей (View)

В классическом MVC View отвечает за формирование ответа клиенту (HTML, JSON, XML). В REST- и API-ориентированных системах этот слой часто называют Presenter или Serializer.

Его задачи:

  • Преобразование результата работы сервиса в формат, понятный клиенту (Domain Object → DTO);
  • Применение политик отображения (например, маскировка полей в зависимости от прав доступа);
  • Формирование структуры ответа (например, JSON:API, HAL, или кастомной).

Представление не должно содержать логики выбора данных — только форматирование. Если в коде презентера встречается if (user.isAdmin) includeSensitiveFields(), это признак нарушения разделения ответственности: фильтрация должна происходить на уровне сервиса или политики доступа.

Эволюция MVC: от MVC к CQRS и Hexagonal Architecture

При росте сложности MVC становится недостаточным. Технический дизайн должен отражать переход к более зрелым архитектурным стилям:

  • CQRS (Command Query Responsibility Segregation) — разделение операций изменения состояния (Commands) и чтения (Queries). Позволяет оптимизировать модели чтения и записи независимо, упрощает масштабирование, поддерживает eventual consistency.

  • Hexagonal Architecture (Ports & Adapters) — домен в центре, окружённый «портами» (интерфейсами), к которым подключаются «адаптеры» (HTTP, БД, очередь). Обеспечивает независимость ядра от инфраструктуры, упрощает тестирование и замену технологий.

В техническом дизайне указание архитектурного стиля — не декларация моды, а обоснованное решение. Например: «Применяется CQRS, поскольку объём данных для отчётов (чтение) на два порядка превышает объём операций записи, и OLTP-БД не справляется с аналитическими запросами. Команды обрабатываются в PostgreSQL, результаты денормализуются в ClickHouse для чтения».


Системное проектирование: от аудита до отказоустойчивости

Системное проектирование — это технический дизайн на уровне взаимодействия систем, а не отдельных модулей. Оно актуально при создании новых сервисов, рефакторинге монолита, интеграции внешних систем. Ниже — ключевые этапы, которые должен охватывать технический дизайн в этом контексте.

Аудит существующих интеграций и зависимостей

Перед изменением системы необходимо понять её текущее состояние. Аудит включает:

  • Картирование зависимостей: какие компоненты вызывают какие, через какие протоколы, с какой частотой и задержкой;
  • Анализ SLA/SLO: какие гарантии дают зависимости, есть ли исторические нарушения;
  • Выявление скрытых связей: например, общая база данных, файловая система, кэш, shared memory;
  • Оценка технического долга: устаревшие протоколы (SOAP 1.1 без WSDL), отсутствие retry-логики, hard-coded endpoints.

Результат — карта системы с цветовой индикацией рисков (красный — критическая точка отказа, жёлтый — устаревший протокол, зелёный — здоровая интеграция). Без такой карты любое изменение — игра в рулетку.

Выделение кандидатов в микросервисы

Переход к микросервисам — не цель, а средство. Технический дизайн должен обосновать выделение конкретного модуля по критериям:

  • Независимость жизненного цикла: модуль развивается, тестируется, развёртывается и масштабируется отдельно;
  • Чёткая ответственность: у модуля есть одна, хорошо определённая область (bounded context);
  • Слабая связанность с остальной системой: минимум синхронных вызовов, чёткий контракт API;
  • Разные нефункциональные требования: например, высокая нагрузка, специфические требования к задержкам, безопасность.

Например, «Модуль генерации отчётов выделяется в отдельный сервис, поскольку требует значительных вычислительных ресурсов (CPU-intensive), имеет асинхронную природу и не должен влиять на latency основного API».

Определение API и форматов обмена данными

API — контракт между системами. В техническом дизайне он описывается не только как набор эндпоинтов, но и как:

  • Семантика операций: idempotency keys, retry-after headers, идемпотентность методов;
  • Форматы данных: JSON Schema (не OpenAPI как замена, а как дополняющий артефакт), версионирование (URI vs header), политики обратной совместимости;
  • Протоколы: REST, gRPC, GraphQL, GraphQL subscriptions, WebSockets — с обоснованием выбора;
  • Безопасность: аутентификация (JWT, OAuth2, mTLS), авторизация (RBAC, ABAC), rate limiting.

Критически: API должен быть специфицирован до начала реализации и согласован со всеми потребителями. Только так возможно параллельное развитие клиентов и сервера.

Проектирование механизмов синхронизации

При распределённых транзакциях или eventual consistency необходимо чётко описать, как достигается согласованность:

  • Двухфазное подтверждение (2PC) — если допустима блокировка и снижение доступности;
  • Saga-паттерн — цепочка локальных транзакций с компенсирующими операциями;
  • Eventual consistency через события — с указанием времени согласования, механизмов детекции конфликтов (vector clocks, CRDT);
  • Transactional Outbox — для надёжной публикации событий в рамках той же транзакции.

Например: «При создании заказа используется Saga: 1) резервирование инвентаря, 2) блокировка средств, 3) подтверждение заказа. При отказе на шаге 2 вызывается компенсация: отмена резервирования инвентаря».

Организация независимых релизных циклов

Микросервисы теряют смысл, если релизятся одновременно. Технический дизайн должен включать:

  • Контрактное тестирование (CDC) — потребители и поставщики тестируются на совместимость контракта;
  • Feature flag’и — для постепенного включения функционала;
  • Canary-релизы и blue/green развёртывания — с описанием метрик, по которым принимается решение о полном переходе;
  • Политики отката — автоматический или ручной, с указанием времени реакции.

Настройка мониторинга и отказоустойчивости

Недостаточно написать «будет Prometheus». В ТД должно быть:

  • SLI/SLO/SLA: например, «99-й перцентиль latency < 500 мс при нагрузке 1000 RPS»;
  • Метрики: что измеряется (latency, error rate, saturation, traffic), как агрегируется, какие алерты срабатывают;
  • Трассировка: требования к сбору span’ов, минимальный уровень детализации;
  • Стратегии отказоустойчивости:
    • retry с jitter и exponential backoff;
    • circuit breaker с настраиваемыми порогами;
    • fallback-ответы (кэшированные, упрощённые);
    • graceful degradation (отключение не критичных фич при высокой нагрузке).

Пример: «При ошибке вызова payment gateway после 2 retry’ев активируется circuit breaker на 30 секунд. В это время возвращается ответ с кодом 503 Service Unavailable и заголовком Retry-After: 30. Логируется событие PaymentGatewayCircuitOpen».


Технический дизайн как документ коммуникации: от черновика к артефакту знаний

Технический дизайн — это не отчёт о проделанной работе и не формальность для прохождения этапа в workflow. Это инструмент совместного мышления, призванный синхронизировать понимание у всех участников проекта до того, как будут затрачены ресурсы на реализацию. Его ценность измеряется не объёмом, а степенью снижения рисков недопонимания, переделок и архитектурных ошибок.

В профессиональной практике дизайн-документ проходит несколько фаз, каждая из которых имеет свою цель и правила.

Фаза 1. Исследование и формулирование гипотез

На этом этапе документ существует в виде рабочего черновика. Его цель — структурировать мысли автора, зафиксировать неопределённости и сформулировать гипотезы. Здесь допустимы:

  • Незавершённые разделы с пометкой «[TBD]»;
  • Вопросы к экспертам («Какова latency между регионами A и B?»);
  • Сравнительные таблицы альтернатив без окончательного выбора.

Ключевой признак здорового черновика — наличие раздела «Открытые вопросы». Если такого раздела нет, велика вероятность, что автор не до конца проработал проблему.

Черновик не отправляется на ревью. Он используется для внутренней проверки или неформального обсуждения с ближайшими коллегами (pair design).

Фаза 2. Согласование концепции

После того как основные гипотезы проверены, а ключевые неопределённости сняты, документ переводится в статус «на концептуальное ревью». На этом этапе проверяются:

  • Корректность интерпретации требований;
  • Обоснованность выбора архитектурного стиля;
  • Учёт критических ограничений (интеграции, безопасность, compliance);
  • Адекватность оценки рисков.

Ревьюеры на этой стадии — архитекторы, тимлиды, представители смежных команд, владельцы зависимых систем. Цель — не проверка синтаксиса, а валидация направления.

Если по итогам концептуального ревью требуется смена подхода (например, переход от синхронного API к событийной модели), документ возвращается в фазу 1. Это не провал — это экономия недель разработки.

Фаза 3. Детализация и техническое ревью (TDR)

После одобрения концепции документ дополняется деталями: спецификацией API, диаграммами, описанием объектов, инфраструктурными требованиями. Затем он проходит техническое дизайн-ревью (TDR).

TDR — это не формальная процедура, а инженерный диспут, направленный на выявление слабых мест в решении. Эффективное TDR строится по следующим принципам:

  • Фокус на решениях, а не на личностях. Комментарии формулируются как «в этом сценарии возможна гонка условий при параллельной обработке», а не «вы не учли конкурентность».

  • Обязательное участие implement’ера. Разработчик, который будет писать код, должен присутствовать и отстаивать свои решения — это развивает инженерную зрелость.

  • Фиксация решений по итогам обсуждения. Все уточнения и изменения вносятся в документ сразу после ревью. История изменений пополняется записью: «2025-11-25 — по итогам TDR добавлено требование к идемпотентности эндпоинта /orders».

  • Явное согласование. Каждый ревьюер ставит отметку (approve / request changes). Отсутствие реакции в течение 48 часов считается согласием по умолчанию — иначе процесс застывает.

TDR завершается либо одобрением документа, либо возвратом на доработку. Важно: ревью не должно длиться более 3–5 рабочих дней. Затягивание — признак неясности требований или отсутствия экспертизы в команде.

Фаза 4. Реализация и актуализация

После одобрения ТД становится руководством к действию. В ходе реализации:

  • Разработчик сверяется с документом при возникновении неопределённостей;
  • Любые отклонения от ТД (например, из-за выявленных технических ограничений) фиксируются в виде patch-запроса к документу и проходят мини-ревью;
  • Тестировщики используют ТД как основу для составления чек-листов и сценариев нагрузочного тестирования.

По завершении задачи документ актуализируется:

  • Удаляются пометки «[TBD]»;
  • В раздел «Реализация» добавляются ссылки на MR/PR, тикеты в трекере;
  • В «Риски» вносятся фактически возникшие проблемы и способы их решения;
  • В «История изменений» фиксируется версия «1.0 — финальная, после реализации».

Актуализированный документ становится частью технической документации продукта. Он отвечает на вопросы:

  • Почему было выбрано именно это решение?
  • Какие альтернативы рассматривались и почему отклонены?
  • Какие компромиссы были приняты?

Такой документ бесценен при onboarding’е новых сотрудников, аудите системы или планировании следующего этапа развития.


Преимущества зрелой практики технического дизайна

Внедрение дисциплины технического проектирования даёт измеримые результаты, подтверждённые многолетней практикой в high-load и enterprise-средах:

  • Сокращение количества критических ошибок на production. Проблемы, выявленные на стадии ревью диаграммы, не требуют hotfix’ей в 3 ночи.

  • Ускорение разработки. Чёткий план снижает когнитивную нагрузку на разработчика, устраняет необходимость постоянных уточнений и согласований в чатах.

  • Повышение предсказуемости сроков. Оценка на основе детального ТД точнее, чем «на глаз» по User Story.

  • Снижение зависимости от ключевых сотрудников. Знания фиксируются в документе, а не в головах.

  • Упрощение аудита и compliance. Требования к безопасности, отказоустойчивости, хранению данных явно задокументированы и проверяемы.

  • Формирование инженерной культуры. Команда учится аргументировать, слушать, искать баланс между идеалом и реальностью.

Важно: технический дизайн не подменяет agile. Он совместим с итеративной разработкой — просто каждая итерация крупной фичи начинается с проработки своей части в виде мини-ТД. Даже задача на два дня может иметь краткий дизайн в виде одного абзаца и диаграммы — если она затрагивает интеграцию или изменяет контракт.


Типичные ошибки и антипаттерны

Несмотря на очевидную пользу, практика технического дизайна часто искажается. Вот наиболее распространённые антипаттерны:

АнтипаттернПризнакиПоследствия
Документ-мумияТД создаётся после реализации, чтобы «закрыть галочку»Не предотвращает ошибок, не служит для коммуникации, быстро устаревает
Роман в прозеСтраницы текста без структуры, диаграмм, чётких формулировокКоллеги не читают, ревью сводится к формальному одобрению
Архитектурная фантазияОписание идеальной системы без учёта текущего состояния, legacy, ограниченийНевозможно реализовать, требует полного переписывания, отвергается бизнесом
Избыточная детализацияУказание конкретных имён классов, имён переменных, строк кодаДокумент ломается при малейшем рефакторинге, отвлекает от сути
Отсутствие альтернативСразу предлагается одно решение без обоснования выбораНевозможно оценить обоснованность, команда не учится анализировать trade-off’ы

Противоядие — умеренность и фокус на цели: «Что должен понять читатель после прочтения этого раздела?» Если ответа нет — раздел можно сократить или удалить.


Пример ТД


# Технический дизайн: Внедрение асинхронной обработки подтверждения заказов

> **Статус**: одобрен
> **Дата последнего изменения**: 2025-11-20
> **Автор**: Тимур Тагиров
> **Команда**: Core Platform

---

## Оглавление

- [Оглавление](#оглавление)
- [История изменений](#история-изменений)
- [Лист согласования](#лист-согласования)
- [Связанные документы](#связанные-документы)
- [Ограничения и допущения](#ограничения-и-допущения)
- [Требования](#требования)
- [Функциональные требования](#функциональные-требования)
- [Нефункциональные требования](#нефункциональные-требования)
- [Текущее состояние](#текущее-состояние)
- [Обзор альтернативных решений](#обзор-альтернативных-решений)
- [Предлагаемое решение](#предлагаемое-решение)
- [Общая архитектура](#общая-архитектура)
- [Детали реализации](#детали-реализации)
- [Объектная модель](#объектная-модель)
- [Диаграммы](#диаграммы)
- [Безопасность и конфиденциальность](#безопасность-и-конфиденциальность)
- [Производительность и отказоустойчивость](#производительность-и-отказоустойчивость)
- [Стоимость](#стоимость)
- [Масштабируемость](#масштабируемость)
- [План реализации](#план-реализации)
- [Ревьюеры](#ревьюеры)
- [Список полезных ссылок](#список-полезных-ссылок)

---

## История изменений

| Версия | Дата | Автор | Изменения |
|--------|------------|-------------------|---------------------------------------------------------------------------|
| 0.1 | 2025-11-15 | Тимур Тагиров | Первоначальный черновик |
| 0.2 | 2025-11-17 | Тимур Тагиров | Добавлены диаграммы, уточнены требования к SLO, описание объектной модели |
| 0.3 | 2025-11-19 | Тимур Тагиров | Учтены замечания по TDR: добавлены fallback-сценарии, уточнён контракт API |
| 1.0 | 2025-11-20 | Тимур Тагиров | Финальная версия после TDR, одобрена |

---

## Лист согласования

| Роль | Имя | Дата | Статус | Комментарий / Ссылка |
|--------------------------|-------------------|------------|------------|----------------------|
| Технический лидер | Алексей Смирнов | 2025-11-18 | ✅ Одобрено ||
| Архитектор инфраструктуры| Елена Петрова | 2025-11-19 | ✅ Одобрено | Согласована схема Kafka topics |
| Владелец заказов | Дмитрий Иванов | 2025-11-18 | ✅ Одобрено | Подтверждено соответствие бизнес-процессу |
| Security Officer | Ольга Козлова | 2025-11-20 | ✅ Одобрено | Требования к шифрованию PII выполнены |

---

## Связанные документы

- [PRD-2025-045] Спецификация бизнес-процесса «Подтверждение заказа»
- [RFC-2024-012] Стандарт обмена событиями в Core Platform (версия 2.1)
- [SEC-POL-007] Политика обработки персональных данных (внутренний регламент)
- [API-STD-003] Стандарт проектирования REST API (версия 3.2)
- [ARCH-DIAG-008] Диаграмма текущего состояния модуля заказов (C4 Level 2)

---

## Ограничения и допущения

- **Допущение**: Внешний payment gateway (`pgw.example.com`) гарантирует idempotency при наличии заголовка `Idempotency-Key`. Подтверждено в документации v3.4.
- **Допущение**: Время обработки подтверждения заказа в сторонних системах (инвентарь, логистика) не превышает 15 секунд в 99% случаев.
- **Ограничение**: Использование новых Kafka features (KIP-848, Tiered Storage) запрещено — кластер обновится не ранее Q2 2026.
- **Ограничение**: Хранение PII (email, ФИО) в логах запрещено политикой SEC-POL-007. Логирование заказа допускается только по `orderId`.
- **Ограничение**: Все микросервисы должны быть развёрнуты в Kubernetes-кластере `prod-eu-west`, multi-AZ.

---

## Требования

### Функциональные требования

1. Пользователь инициирует подтверждение заказа через REST API (`POST /orders/{id}/confirm`).
2. Система валидирует состояние заказа: подтверждение возможно только для заказов в статусе `AWAITING_CONFIRMATION`.
3. Система резервирует товар в инвентаре через синхронный вызов `InventoryService.reserve()`.
4. При успешной резервации — инициирует асинхронную оплату через `PaymentService.chargeAsync()`.
5. По завершении оплаты (успех/отказ) — обновляет статус заказа и уведомляет пользователя.
6. В случае временного отказа `InventoryService` — повторяет вызов до 3 раз с экспоненциальной задержкой (1s, 2s, 4s).
7. В случае постоянного отказа `InventoryService` — переводит заказ в статус `RESERVATION_FAILED` и фиксирует событие `OrderReservationFailed`.
8. Поддержка idempotency: повторный вызов `/confirm` с тем же `Idempotency-Key` не приводит к дублированию операций.

### Нефункциональные требования

| Категория | Требование |
|-------------------------|-----------------------------------------------------------------------------|
| **Производительность** | 95-й перцентиль latency подтверждения ≤ 300 мс при нагрузке 200 RPS |
| **Надёжность** | SLO: 99.95% успешных подтверждений в течение суток |
| **Отказоустойчивость** | При отказе `PaymentService` система остаётся работоспособной: заказ переходит в статус `PAYMENT_PENDING`, оплата повторяется по расписанию |
| **Масштабируемость** | Поддержка пиковой нагрузки до 1000 RPS без горизонтального масштабирования |
| **Безопасность** | Все вызовы к `PaymentService` — по mTLS; `Idempotency-Key` валидируется на длину (36 символов, UUIDv4) |
| **Сопровождаемость** | Каждый этап обработки логируется с `traceId`; события публикуются в Kafka для анализа |

---

## Текущее состояние

В настоящий момент подтверждение заказа реализовано как **синхронный монолитный flow** в сервисе `OrderService`:

1. `POST /orders/{id}/confirm`
2. Вызов `InventoryService.reserve()` (sync) →
3. Вызов `PaymentService.charge()` (sync) →
4. Обновление статуса в БД →
5. Отправка email →
6. Возврат ответа клиенту.

**Проблемы**:
- Высокая latency: 95-й перцентиль — 1.2 с (превышает SLA 500 мс).
- Низкая отказоустойчивость: при отказе `PaymentService` клиент получает 5xx, заказ остаётся в `AWAITING_CONFIRMATION`, требует ручного вмешательства.
- Блокировка ресурсов: пока ждём ответ от `PaymentService`, соединение и транзакция БД удерживаются.
- Плохая масштабируемость: рост RPS линейно увеличивает load на `OrderService`.

Диаграмма текущего состояния: [ссылка на ARCH-DIAG-008#current-flow].

---

## Обзор альтернативных решений

| Решение | Плюсы | Минусы | Риск |
|----------------------------------|-----------------------------------------------------------------------|------------------------------------------------------------------------|---------------------------------------|
| **1. Синхронный flow с таймаутами** | Минимальные изменения кода | Не решает проблему блокировки; при таймауте — неопределённое состояние | Высокий: клиент не знает, проведена ли оплата |
| **2. Eventual consistency через Kafka** | Высокая отказоустойчивость; чёткий audit trail; масштабируемость | Увеличение сложности; latency подтверждения для клиента — до 2–3 с | Средний: требует обработки дубликатов событий |
| **3. Saga с компенсирующими транзакциями** | Гарантированная согласованность; подходит для критичных операций | Высокая сложность реализации; необходимость хранения состояния Saga | Высокий: ошибка в компенсации — потеря данных |

**Выбор**: Решение **2 (Eventual consistency через Kafka)**.
**Обоснование**:
- Соответствует стратегии перехода к event-driven архитектуре (см. RFC-2024-012).
- Отвечает требованиям по отказоустойчивости и масштабируемости.
- Время подтверждения для клиента не критично: в UX предусмотрено состояние *«Заказ обрабатывается»*.
- Команда имеет экспертизу по Kafka (проекты Payments, Notifications).

---

## Предлагаемое решение

### Общая архитектура

Система переходит на **асинхронную, событийно-ориентированную модель** с использованием Kafka в качестве шины сообщений. Основные компоненты:

1. **Order API Gateway** — точка входа, валидация, генерация `Idempotency-Key`, публикация события `OrderConfirmationRequested`.
2. **Order Orchestration Service** — потребитель события, оркестрирует flow: резервирование → инициация оплаты → обработка результата.
3. **Inventory Service** — остаётся синхронным (требуется немедленная резервация).
4. **Payment Service** — обрабатывает оплату асинхронно, публикует события `PaymentSucceeded` / `PaymentFailed`.
5. **Order State Service** — поддерживает актуальный статус заказа, обновляет БД, инициирует уведомления.

Взаимодействие:
`Client``Order API Gateway` → (Kafka: `order-events`) → `Order Orchestration Service``Inventory Service` (sync) → `Payment Service` (async) → (Kafka: `payment-events`) → `Order Orchestration Service``Order State Service`.

### Детали реализации

#### 1. Idempotency
- Клиент предоставляет `Idempotency-Key` в заголовке (UUIDv4).
- `Order API Gateway` проверяет наличие ключа в Redis (`idempotency:{key}` TTL=24h).
- При повторном запросе — возвращает кэшированный ответ без повторной обработки.

#### 2. Резервирование инвентаря
- Вызов `InventoryService.reserve(orderId, items)` с таймаутом 5s.
- Retry: 3 попытки, backoff 1s → 2s → 4s.
- При отказе — публикация `OrderReservationFailed`, переход заказа в `RESERVATION_FAILED`.

#### 3. Асинхронная оплата
- `Order Orchestration Service` вызывает `PaymentService.chargeAsync(orderId, amount)`.
- `PaymentService` возвращает `202 Accepted`, запускает фоновую задачу.
- По завершении — публикует событие в топик `payments`.
- События:
- `PaymentSucceeded { orderId, paymentId, timestamp }`
- `PaymentFailed { orderId, reason, retryCount }`

#### 4. Обработка результата оплаты
- `Order Orchestration Service` потребляет события из `payments`.
- При `PaymentSucceeded` → вызов `OrderStateService.confirm(orderId)`.
- При `PaymentFailed` с `retryCount < 3` → повторный вызов `chargeAsync`.
- При `PaymentFailed` с `retryCount = 3` → переход в `PAYMENT_FAILED`, уведомление оператора.

### Объектная модель

#### Объект `OrderConfirmationProcess`

| Аспект | Описание |
|---------------|--------------------------------------------------------------------------|
| **Атрибуты** | `processId` (UUID), `orderId`, `status` (`PENDING`, `RESERVED`, `PAYMENT_INITIATED`, `COMPLETED`, `FAILED`), `createdAt`, `updatedAt`, `idempotencyKey` |
| **Методы** | `initiate()`, `reserveInventory()`, `initiatePayment()`, `handlePaymentSuccess()`, `handlePaymentFailure()`, `complete()`, `fail(reason)` |
| **Статусы** | Диаграмма состояний: [ссылка на диаграмму ниже] |
| **События** | Публикует: `OrderConfirmationStarted`, `InventoryReserved`, `PaymentInitiated`, `OrderConfirmed`, `OrderConfirmationFailed` |

#### Объект `PaymentAttempt`

| Аспект | Описание |
|---------------|--------------------------------------------------------------------------|
| **Атрибуты** | `attemptId`, `orderId`, `paymentId`, `status` (`PENDING`, `SUCCEEDED`, `FAILED`), `retryCount`, `lastError` |
| **Методы** | `execute()`, `retry()`, `markAsSucceeded()`, `markAsFailed(reason)` |
| **События** | Публикует: `PaymentAttemptStarted`, `PaymentAttemptSucceeded`, `PaymentAttemptFailed` |

### Диаграммы

#### Диаграмма последовательностей: Успешное подтверждение


Client Order API Gateway Order Orchestration Inventory Service Payment Service
│ │ │ │ │
│ POST /confirm │ │ │ │
│────────────────────────────>│ │ │ │
│ │ Check idempotency │ │ │
│ │─────────────────────────>│ │ │
│ │<─────────────────────────│
Publish OrderConfirmationRequested
│──────────────────────────────────────>│ │ │
│ │ │ reserveInventory() │ │
│ │ │───────────────────────>│ │
│ │ │<───────────────────────│
initiatePayment()
│─────────────────────────────────────────────>
│ │ │ │ │
│ 202 Accepted │ │ │ │
<────────────────────────────│
Charge processing
│─────────────────────>
│ │ │ │ │
│ │ │<── PaymentSucceeded ──│
confirmOrder()
│───────────────────────>│ │
│ │ │ │ │


#### Диаграмма состояний: `OrderConfirmationProcess`


[PENDING]


[RESERVED] ──(оплата инициирована)──► [PAYMENT_INITIATED]
│ │
│ (резервация не удалась) │ (оплата успешна)
▼ ▼
[RESERVATION_FAILED] [COMPLETED]

│ (оплата не удалась, 3 попытки)

[PAYMENT_FAILED]


#### Диаграмма компонентов


┌──────────────────────┐ ┌───────────────────────────┐ ┌───────────────────┐
│ │ │ │ │ │
│ Order API Gateway │────▶│ Order Orchestration │────▶│ Inventory Service │
│ │ │ Service │ │ (sync HTTP) │
└──────────────────────┘ └─────────────┬─────────────┘ └───────────────────┘


┌──────────────────────┐
│ │
│ Payment Service │
│ (async, events) │
└──────────┬───────────┘


┌──────────────────────┐
│ │
│ Order State │
│ Service │
└──────────────────────┘


---

## Безопасность и конфиденциальность

- Все вызовы между сервисами — по mTLS (сертификаты управляются HashiCorp Vault).
- События в Kafka — шифруются на уровне приложения (AES-GCM, ключи в Vault).
- PII (email, ФИО) не включаются в события и логи. В событиях используется только `orderId`.
- `Idempotency-Key` валидируется: длина 36, формат UUIDv4. Невалидные ключи — 400 Bad Request.
- Rate limiting: 10 запросов/минута на `orderId` (защита от флуда).

---

## Производительность и отказоустойчивость

- **SLO**: 99.95% успешных подтверждений за 24 часа.
- **SLI**:
- `success_rate = (confirmed + failed_gracefully) / total_requests`
- `latency_p95 ≤ 300 мс` (от запроса до 202 Accepted)
- **Механизмы**:
- Circuit breaker для `InventoryService` (открытие при 5 ошибках за 30с, сброс через 60с).
- Retry с jitter для `PaymentService`.
- Fallback: при отказе `PaymentService` — заказ в `PAYMENT_PENDING`, повтор через 5/10/30 мин.
- **Мониторинг**:
- Метрики: `order_confirmation_requests_total`, `order_confirmation_errors_total`, `inventory_reservation_latency_seconds`.
- Алерты: при `error_rate > 0.5%` за 5 минут, `latency_p95 > 500 мс`.

---

## Стоимость

| Статья | Оценка |
|--------------------------|-----------------------------------------|
| **Инфраструктура** | +2 Kafka partitions, +1 consumer group → ~$15/мес (AWS MSK) |
| **Разработка** | 10 человеко-дней (2 разработчика × 5 дней) |
| **Тестирование** | 3 человеко-дня (нагрузочное + интеграционное) |
| **Поддержка** | Минимальная: события упрощают диагностику; автоматизация retry снижает ручные операции |
| **Расширяемость** | Архитектура позволяет легко добавить новые этапы (например, проверку мошенничества) |

---

## Масштабируемость

- **Горизонтальное масштабирование**:
- `Order Orchestration Service` — stateless, масштабируется по CPU (target: 70%).
- Потребление из Kafka — через consumer groups, масштабируется числом инстансов.
- **Вертикальное масштабирование**:
- БД `order_state` — read replicas для отчётов.
- Redis для idempotency — cluster mode.
- **План на 5× нагрузку**:
- Увеличение партиций Kafka до 10.
- Настройка приоритезации: high-priority для новых заказов, low-priority для retry.

---

## План реализации

| Этап | Зависимости | Сроки | Ресурсы |
|-------------------------------|---------------------------------|-----------|-----------------------|
| 1. Подготовка инфраструктуры || 1 день | DevOps |
| 2. Реализация idempotency | Redis cluster настроен | 2 дня | 1 разработчик |
| 3. Рефакторинг Order Service | API Gateway готов | 3 дня | 1 разработчик |
| 4. Интеграция с Payment | Payment Service поддерживает async | 2 дня | 1 разработчик |
| 5. Тестирование и TDR | Все компоненты собраны | 2 дня | QA + команда |
| **Итого** | | **10 дней** | |

---

## Ревьюеры

- [ ] Алексей Смирнов (Tech Lead)
- [ ] Елена Петрова (Infra Architect)
- [ ] Дмитрий Иванов (Product Owner)
- [ ] Ольга Козлова (Security)
- [x] Тимур Тагиров (Author)

---

## Список полезных ссылок

- [RFC-2024-012] Стандарт обмена событиями: https://rfc.corp.local/2024/012
- Kafka Best Practices (Confluent): https://docs.confluent.io/platform/current/design.html
- Idempotency Keys in Stripe API: https://stripe.com/docs/api/idempotent_requests
- Saga Pattern (Chris Richardson): https://microservices.io/patterns/data/saga.html
- Google SRE Workbook: https://sre.google/workbook/table-of-contents/